package com.jonlandrum.selfbalancingtree.splaytree;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.jonlandrum.selfbalancingtree.Node;

public class SplayTreeTest {
    private SplayTree<String> tree;
    
    @Before
    public void init() {
        tree = new SplayTree<String>();
    }
    
    @After
    public void destroy() {
        tree = null;
    }
    
    @Test
    public void testConstructorNull() {
        assertThat(tree, instanceOf(SplayTree.class));
    }
    
    @Test
    public void testConstructorString() {
        tree = new SplayTree<String>("Root");
        assertThat(tree, instanceOf(SplayTree.class));
    }
    
    @Test
    public void testConstructorNode() {
        Node<String> root = new Node<String>("Root");
        tree = new SplayTree<String>(root);
        assertThat(tree, instanceOf(SplayTree.class));
    }
    
    @Test
    public void testAddElementString() {
        assertFalse(tree.hasElement("Root"));
        tree.addElement("Root");
        assertTrue(tree.hasElement("Root"));
        tree.addElement("Child");
        assertEquals("Child", tree.getRoot().getData());
        assertEquals("Root", tree.getRoot().getRightChild().getData());
        assertTrue(tree.hasElement("Root"));
        assertTrue(tree.hasElement("Child"));
    }
    
    @Test
    public void testAddElementNode() {
        Node<String> root = new Node<String>("Root");
        Node<String> child = new Node<String>("Child");
        assertFalse(tree.hasElement(root));
        tree.addElement(root);
        assertTrue(tree.hasElement(root));
        assertEquals("Root", tree.getRoot().getData());
        tree.addElement(child);
        assertTrue(tree.hasElement(root));
        assertTrue(tree.hasElement(child));
        assertEquals("Child", tree.getRoot().getData());
        assertEquals("Root", tree.getRoot().getRightChild().getData());
    }
    
    @Test
    public void testFindElementString() {
        assertEquals(null, tree.findElement("Root").getData());
        tree.addElement("Root");
        tree.addElement("Child");
        assertEquals("Child", tree.getRoot().getData());
        assertEquals("Root", tree.findElement("Root").getData());
        assertEquals("Root", tree.getRoot().getData());
    }
    
    @Test
    public void testFindElementNode() {
        Node<String> root = new Node<String>("Root");
        Node<String> child = new Node<String>("Child");
        assertEquals(null, tree.findElement(root).getData());
        tree.addElement(root);
        tree.addElement(child);
        assertEquals("Child", tree.getRoot().getData());
        assertEquals("Root", tree.findElement(root).getData());
        assertEquals("Root", tree.getRoot().getData());
    }
    
    @Test
    public void testRemoveElementString() {
        tree = new SplayTree<String>("Root");
        tree.addElement("Child");
        assertTrue(tree.hasElement("Root"));
        assertTrue(tree.hasElement("Child"));
        assertEquals("Child", tree.getRoot().getData());
        assertEquals("Root", tree.getRoot().getRightChild().getData());
        tree.removeElement("Child");
        assertFalse(tree.hasElement("Child"));
        assertTrue(tree.hasElement("Root"));
        assertEquals("Root", tree.getRoot().getData());
        tree.removeElement("Root");
        Node<String> root = new Node<String>("Root");
        Node<String> child1 = new Node<String>("Child 1");
        Node<String> child2 = new Node<String>("Child 2");
        tree.addElement(root);
        tree.addElement(child1);
        tree.addElement(child2);
        assertEquals(child2, tree.getRoot());
        assertEquals(child1, tree.getRoot().getLeftChild());
        assertEquals(root, tree.getRoot().getRightChild());
        tree.removeElement("Root");
        assertFalse(tree.hasElement(root));
    }
    
    @Test
    public void testRemoveElementNode() {
        Node<String> root = new Node<String>("Root");
        Node<String> child = new Node<String>("Child");
        tree = new SplayTree<String>(root);
        tree.addElement(child);
        assertTrue(tree.hasElement(root));
        assertTrue(tree.hasElement(child));
        tree.removeElement(root);
        assertFalse(tree.hasElement(root));
        assertTrue(tree.hasElement(child));
        assertEquals("Child", tree.getRoot().getData());
    }
    
    @Test
    public void testSplay() {
        SplayTree<Integer> tree = new SplayTree<Integer>();
        tree.addElement(4);
        tree.addElement(2);
        assertEquals((Integer) 2, tree.getRoot().getData());
        assertEquals((Integer) 4, tree.getRoot().getRightChild().getData());
        tree.addElement(1);
        assertEquals((Integer) 1, tree.getRoot().getData());
        assertEquals((Integer) 2, tree.getRoot().getRightChild().getData());
        assertEquals((Integer) 4, tree.getRoot().getRightChild().getRightChild().getData());
        tree.addElement(3);
        assertEquals((Integer) 3, tree.getRoot().getData());
        assertEquals((Integer) 1, tree.getRoot().getLeftChild().getData());
        assertEquals((Integer) 2, tree.getRoot().getLeftChild().getRightChild().getData());
        assertEquals((Integer) 4, tree.getRoot().getRightChild().getData());
        tree.addElement(6);
        tree.addElement(5);
        tree.addElement(7);
        assertEquals((Integer) 7, tree.getRoot().getData());
        tree.removeElement(4);
        assertEquals((Integer) 5, tree.getRoot().getData());
        assertEquals((Integer) 3, tree.getRoot().getLeftChild().getData());
        assertEquals((Integer) 1, tree.getRoot().getLeftChild().getLeftChild().getData());
        assertEquals((Integer) 2, tree.getRoot().getLeftChild().getLeftChild().getRightChild().getData());
        assertEquals((Integer) 7, tree.getRoot().getRightChild().getData());
        assertEquals((Integer) 6, tree.getRoot().getRightChild().getLeftChild().getData());
    }
}